﻿// MyProject.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
//公众号：程序员速成 ，内含一辈子都让你感激自己的优质视频教程，欢迎关注

#include <iostream>
#include <vector>
#include <string>
#include <fstream>

#ifdef _DEBUG   //只在Debug（调试）模式下
#ifndef DEBUG_NEW
#define DEBUG_NEW new(_NORMAL_BLOCK,__FILE__,__LINE__) //重新定义new运算符
#define new DEBUG_NEW
#endif
#endif

//#include <boost/type_index.hpp>
using namespace std;
//#pragma warning(disable : 4996) 

namespace _nmsp1
{
	class WebAddr
	{
	public:
		virtual void visit() = 0; //执行访问网站的动作，子类中会重新实现
		virtual ~WebAddr() {} //做父类时析构函数应该为虚函数
	};
	//某购物网站
	class WebAddr_Shopping :public WebAddr
	{
	public:
		virtual void visit()
		{
			//......访问该购物网站，可能涉及复杂的网络通信
			cout << "访问WebAddr_Shopping购物网站！" << endl;
		}
	};
	//某视频网站
	class WebAddr_Video :public WebAddr
	{
	public:
		virtual void visit()
		{
			//......访问该视频网站，可能涉及复杂的网络通信
			cout << "访问WebAddr_Video视频网站！" << endl;
		}
	};

	//-------------------------
	//网站代码类
	class WebAddrProxy :public WebAddr
	{
	public:
		//构造函数，引入的目的是传递进来要访问的具体网站
		WebAddrProxy(WebAddr* pwebaddr) :mp_webaddr(pwebaddr) {}

	public:
		virtual void visit()
		{
			//在这里进行访问的合法性检查，日志记录或者流量限制......
			mp_webaddr->visit();
			//在这里可以进行针对返回数据的过滤......
		}

	private:
		WebAddr* mp_webaddr; //代码要访问的具体网站
	};
	
	//-----------------
	//专门针对某购物网站WebAddr_Shopping的代理
	class WebAddr_Shopping_Proxy :public WebAddr
	{
	public:
		virtual void visit()
		{
			//在这里进行访问的合法性检查，日志记录或者流量限制......
			WebAddr_Shopping* p_webaddr = new WebAddr_Shopping();
			p_webaddr->visit();			
			//在这里可以进行针对返回数据的过滤......

			//释放资源
			delete p_webaddr;
		}
	};
}

namespace _nmsp2
{
	//缓存/缓冲代理（Cache Proxy）范例
	vector<string> g_fileItemList; 

	//抽象主题
	class ReadInfo
	{
	public:
		virtual void read() = 0;
		virtual ~ReadInfo() {}
	};
	//真实主题
	class ReadInfoFromFile : public ReadInfo
	{
	public:
		virtual void read() //从文件中读信息（读取test.txt的内容）
		{
			ifstream fin("test.txt");
			if (!fin)
			{
				cout << "文件打开失败" << endl;
				return;
			}
			string linebuf;
			while (getline(fin, linebuf)) //从哪个文件中逐行读入内容
			{
				if (!linebuf.empty()) //读的不是空行
				{
					g_fileItemList.push_back(linebuf); //将文件中的每一行都保存到vector容器中。
					//cout << linebuf << endl;
				}
			}
			fin.close(); //关闭文件输入流
		}
	};
	//代理主题
	class ReadInfoProxy :public ReadInfo
	{
	public:
		virtual void read()
		{
			if (!m_loaded)
			{
				//没有从文件中载入信息，则载入
				m_loaded = true; //标记已经从文件中载入信息了
				cout << "从文件中读取了如下数据---------：" << endl;
				ReadInfoFromFile* rf = new ReadInfoFromFile();
				rf->read(); //将文件中的数据读入全局容器g_fileItemList
				delete rf; //释放资源
			}
			else
			{
				cout << "从缓存中读取了如下数据----------：" << endl;
			}
			//现在数据一定在g_fileItemList中，开始显示
			for (auto iter = g_fileItemList.begin(); iter != g_fileItemList.end(); ++iter)
			{
				cout << *iter << endl;
			}
		}
	private:
		bool m_loaded = false; //false表示还没有从文件中读出数据到内存
	};

}

int main()
{
	_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);//程序退出时检测内存泄漏并显示到“输出”窗口
	
	//第14章 代理（Proxy）模式：结构型模式
	//目的：为客户端增加额外的功能、约束或针对客户端的调用屏蔽一些复杂的细节问题。
	//（1）基本概念和范例
	//通过引入代理类来为原始类（被代理类）增加额外的能力——新功能、新服务，约束或者限制。
	//使用代理类的程序员与开发这个代理类的程序员并不是同一个程序员甚至可能是两个不同公司的程序员。

	//（2）引入代理（Proxy）模式：
	//定义（实现意图）：为其他对象提供一种代理以控制对这个对象的访问。
	//三种角色：
	//a)Subject（抽象主题），WebAddr类。
	//b)Proxy（代理主题）：WebAddr_Shopping_Proxy类。
	//c)RealSubject（真实主题）：WebAddr_Shopping

	//代理模式和装饰模式对比；
	//代理模式：代表真实主题并给真实主题增加一些新能力或者责任，这些功能与真实主题要实现的功能可能没有相关性，主要是控制对真实主题的访问。
	//装饰模式：新功能与原有构件能力具有相关性，是对原有构件能力或者行为的扩展（增强）。

	//（3）代理模式的应用场合探究
	//（ 3.1 ）代理模式常用应用场景
	//常见的代理包括但不限于如下这些类型：
	//a)远程代理（Remote Proxy）
	//b)虚拟代理（Virtual Proxy)
	//c)保护代理（Protect Proxy）
	//d)缓存/缓冲代理（Cache Proxy）
	//e)智能引用代理（Smart Reference Proxy）。shared_ptr
	//f)写时复制（copy-on-write)优化代理：string
	//g)防火墙代理、同步代理，复杂隐藏代理等。

	//（ 3.2 ）缓存 / 缓冲代理（Cache Proxy）一例







	/*
	_nmsp1::WebAddr* wba1 = new _nmsp1::WebAddr_Shopping();
	wba1->visit(); //访问该购物网站

	_nmsp1::WebAddr* wba2 = new _nmsp1::WebAddr_Video();
	wba2->visit(); //访问该视频网站

	//资源释放
	delete wba1;
	delete wba2;
	*/

	/*
	_nmsp1::WebAddr* wba1 = new _nmsp1::WebAddr_Shopping();
	_nmsp1::WebAddr* wba2 = new _nmsp1::WebAddr_Video();

	_nmsp1::WebAddrProxy* wbaproxy1 = new _nmsp1::WebAddrProxy(wba1);
	wbaproxy1->visit(); //通过代理去访问WebAddr_Shopping购物网站

	_nmsp1::WebAddrProxy* wbaproxy2 = new _nmsp1::WebAddrProxy(wba2);
	wbaproxy2->visit(); //通过代理去访问WebAddr_Video视频购物网站

	//资源释放
	delete wba1;
	delete wba2;
	//资源释放
	delete wbaproxy1;
	delete wbaproxy2;
	*/

	/*
	_nmsp1::WebAddr_Shopping_Proxy* wbasproxy = new _nmsp1::WebAddr_Shopping_Proxy();
	wbasproxy->visit(); //访问实际的购物网站

	//资源释放
	delete wbasproxy;
	*/
	
	_nmsp2::ReadInfo* preadinfoproxy = new _nmsp2::ReadInfoProxy();
	preadinfoproxy->read(); //第一次调用read是借助代理使用真实主题到文件中拿数据
	preadinfoproxy->read(); //后续调用read都是直接从缓存中拿数据
	preadinfoproxy->read(); //从缓存中拿数据

	//资源释放
	delete preadinfoproxy;
	system("pause");
	return 0;
}

// 运行程序: Ctrl + F5 或调试 >“开始执行(不调试)”菜单
// 调试程序: F5 或调试 >“开始调试”菜单

// 入门使用技巧: 
//   1. 使用解决方案资源管理器窗口添加/管理文件
//   2. 使用团队资源管理器窗口连接到源代码管理
//   3. 使用输出窗口查看生成输出和其他消息
//   4. 使用错误列表窗口查看错误
//   5. 转到“项目”>“添加新项”以创建新的代码文件，或转到“项目”>“添加现有项”以将现有代码文件添加到项目
//   6. 将来，若要再次打开此项目，请转到“文件”>“打开”>“项目”并选择 .sln 文件

